<template>
  <el-card class="config-section">
      <h3>开发者</h3>
      <el-form label-width="90px">
        <el-form-item label="公链：">
      <!-- 合约地址 -->
      <el-select v-model="chain" placeholder="选择公链">
        <el-option
          v-for="chain in chainOptions"
          :key="chain.value"
          :label="chain.label"
          :value="chain.value"
        />
      </el-select>
    </el-form-item>

    <el-form-item label="节点选择：">
      <el-select 
        v-model="rpc"
        placeholder="选择RPC节点"
        class="mt-20"
      >
        <el-option
          v-for="rpc in rpcOptions[chain]"
          :key="rpc"
          :label="rpc"
          :value="rpc"
        />
      </el-select>
    </el-form-item>
  </el-form>
      
    </el-card>

  <el-card class="trade">
    <h3>交易配置</h3>
    
    <el-form label-width="90px">
      <!-- 合约地址 -->
      <el-form-item label="交易模式：">
        <el-radio-group v-model="mode" @change="handleMode">
              <el-radio-button value="pump">买入</el-radio-button>
              <el-radio-button value="dump">卖出</el-radio-button>
              <el-radio-button value="mixed">混合</el-radio-button>
              <!-- <el-radio-button value="volume">刷交易量</el-radio-button>
              <el-radio-button value="swing">高抛低吸</el-radio-button> -->
            </el-radio-group>
      </el-form-item>
      <!-- 合约地址 -->
      <el-form-item label="合约地址：">
        <el-input v-model="contractAddress" placeholder="输入合约地址" @keyup="getToken" />
      </el-form-item>
      <!-- 代币名称 -->
      <el-form-item label="代币信息：">
      <b>{{token.pair}}</b> <span v-if="token.price">，当前价格：{{token.price}}</span>
      </el-form-item>
      <!-- 池子类型 -->
      <el-form-item label="池子类型：">
        <el-radio-group v-model="poolType">
          <el-radio value="bnb">BNB (BSC)</el-radio>
          <el-radio value="usdt">USDT (BSC)</el-radio>
        </el-radio-group>
      </el-form-item>

      <!-- 目标价格 -->
      <el-form-item label="目标价格：">
        <el-input-number 
          v-model="targetPrice"
          controls-position="right"
          :step="0.00000001"
        />
      </el-form-item>

      <!-- 买入设置 -->
      <el-form-item label="买入方式：">
        <el-radio-group v-model="buyMethod">
          <el-radio value="amount">金额</el-radio>
          <el-radio value="quantity">数量</el-radio>
        </el-radio-group>
      </el-form-item>
      

      <el-form-item label="Gas费：">
        <el-input-number 
          v-model="gas"
          controls-position="right"
          :step="0.0001"
        />
        &nbsp; BNB
      </el-form-item>

      <el-form-item label="买入金额：" v-if="mode=='pump'||mode=='mixed'">
        <el-input-number v-model="buyMin" :min="0" /> &nbsp;&nbsp;至&nbsp;&nbsp; 
        <el-input-number v-model="buyMax" :min="buyMin" />
        <span class="unit">{{ buyMethod === 'amount' ? 'BNB' : '数量' }}</span>
      </el-form-item>

      <!-- 高级设置 -->
      <el-form-item label="买入间隔：" v-if="mode=='pump'||mode=='mixed'">
        <el-input-number v-model="buyIntervalMin" :min="5" /> &nbsp;&nbsp;至&nbsp;&nbsp; 
        <el-input-number v-model="buyIntervalMax" :min="buyIntervalMin" />
         &nbsp;&nbsp;秒
      </el-form-item>

      <!-- 高级设置 -->
      <el-form-item label="卖出间隔：" v-if="mode=='dump'||mode=='mixed'">
        <el-input-number v-model="sellIntervalMin" :min="5" /> &nbsp;&nbsp;至&nbsp;&nbsp; 
        <el-input-number v-model="sellIntervalMax" :min="sellIntervalMin" />
         &nbsp;&nbsp;秒
      </el-form-item>

      <el-form-item label="交易滑点：">
        <el-input-number 
          v-model="slippage"
          :min="0.1"
          :max="50"
          :precision="1"
        />&nbsp;&nbsp;%
      </el-form-item>
         <!--  <el-form-item label="并发交易：">
            <el-switch v-model="enableConcurrent" />
          </el-form-item> -->
    </el-form>

  </el-card>
</template>

<script setup>
import { onMounted, ref,inject} from 'vue'
import client from '../utils/web3Client'
import { ElMessage } from 'element-plus'
import BigNumber from 'bignumber.js'
import { fmt } from '@/utils/string'
import { useConfigStore } from '@/stores/configStore'
const config = useConfigStore()

const logger = inject('logger');
const accounts = inject('accounts');

const collectAddress = inject('collectAddress');
const privateKey = inject('privateKey');
const address = inject('address');
const indexRunning = inject('indexRunning');

// 转账
const contractAddress = ref('')
const token = ref({})
const poolType = ref('bnb')
const targetPrice = ref(1)
const buyMethod = ref('amount')
const buyMin = ref(0.0015)
const buyMax = ref(0.018)
const gas = ref(0.00015)
const buyIntervalMin = ref(5)
const buyIntervalMax= ref(10)
const sellIntervalMin = ref(10)
const sellIntervalMax= ref(20)
const slippage = ref(0.5)
const enableConcurrent = ref(false)


let status='running';
let lock=false;

const mode = ref('pump')

const chain = ref('bsc')
const chainOptions = [
  { value: 'bsc', label: 'BSC' },
  { value: 'ethereum', label: 'Ethereum' }
]

const rpc=ref('https://bsc-dataseed.binance.org/');

const rpcOptions = {
  bsc:  ["https://bsc-dataseed.binance.org/", "https://bsc-dataseed1.defibit.io/", "https://bsc-dataseed1.ninicoin.io/", "https://bsc-dataseed2.defibit.io/", "https://bsc-dataseed3.defibit.io/", "https://bsc-dataseed4.defibit.io/", "https://bsc-dataseed2.ninicoin.io/", "https://bsc-dataseed3.ninicoin.io/", "https://bsc-dataseed4.ninicoin.io/", "https://bsc-dataseed1.binance.org/", "https://bsc-dataseed2.binance.org/", "https://bsc-dataseed3.binance.org/", "https://bsc-dataseed4.binance.org/", "https://rpc.ankr.com/bsc", "https://bscrpc.com", "https://binance.nodereal.io", "https://rpc-bsc.bnb48.club"]
  ,
  ethereum: ["https://eth-mainnet.g.alchemy.com/v2/vRK5VXqPVxE8KWMo-gwlogKIBosvQW6m", "https://api.mycryptoapi.com/eth", "https://rpc.flashbots.net/", "https://cloudflare-eth.com/", "https://mainnet-nethermind.blockscout.com/", "https://nodes.mewapi.io/rpc/eth", "https://main-rpc.linkpool.io/", "https://mainnet.eth.cloud.ava.do/", "https://ethereumnodelight.app.runonflux.io", "https://rpc.ankr.com/eth", "https://eth-rpc.gateway.pokt.network", "https://main-light.eth.linkpool.io", "https://eth-mainnet.public.blastapi.io"]
}


onMounted(() => {
  client.init(rpc.value,logger)
})

const check = () => {
  if (!privateKey.value) {
    throw new Error('请设置主钱包私钥');
  }
  if (!address.value) {
    throw new Error('请先解析钱包');
  }

  if (!chain.value || !rpc.value || !mode.value) {
    throw new Error('链配置不全');
  }

  if (!contractAddress.value) {
    throw new Error('缺少合约地址');
  }

  if (!buyIntervalMin.value||buyIntervalMin.value<1) {
    throw new Error('时间间隔不能小于1秒');
  }
}


const startTrading = async () => {
  // check()
  const config=getConfig();
  logger.value.info("配置",JSON.stringify(config, null, 2))
  if(mode.value=='pump'){
    buyTask();
    return;
  }
  if(mode.value=='dump'){
    sellTask();
    return;
  }

  if(mode.value=='mixed'){
    buyTask();
    sellTask();
    return;
  }
}


const buyTask = async () => {
  logger.value.info("买入","开始执行")
  status='running';
  let lock=false;
  let count=0;
  while(true){
    if(status=='stop'||mode.value=='dump'){
      logger.value.info("买入","停止执行")
      break;
    }
    count++;
    if(count==1){
      logger.value.info('买入',`等待 5s`)
      await sleep(5*1000);
    }else{
      const intervalTime=getRandomIntInclusive(buyIntervalMin.value,buyIntervalMax.value);
      logger.value.info('买入',`等待 ${intervalTime}s`)
      await sleep(intervalTime*1000);
    }
    if(lock){
      continue;
    }
    lock=true;
    try{
      if(status=='stop'){
        stopTrading()
        break;
      }
      logger.value.info('买入',`当前价格：${token.value.price}`);
      if(token.value.price&&BigNumber(token.value.price).gt(BigNumber(targetPrice.value))){
        logger.value.error('买入',`当前价格：${token.value.price}\n目标价格：${targetPrice.value}\n当前价格 > 目标价格,停止`)
        lock=false;
        stopTrading()
        break;
      }
      await buy()
      if(status=='stop'){
        stopTrading()
        break;
      }
    } catch (error) {
      if(error.message=='账户余额不足，需大于 0.01 BNB'){
        stopTrading();
        break;
      }
    }finally{
      lock=false;
    }
  }
}

const sellTask = async () => {
  logger.value.info("卖出","开始执行")
  logger.value.info("卖出",`卖出间隔 [${sellIntervalMin.value},${sellIntervalMax.value}]`)
  status='running';
  let lock=false;
  let count=0;
  while(true){
    if(status=='stop'||mode.value=='pump'){
      logger.value.info("卖出","停止执行")
      break;
    }
    count++;
    const intervalTime=getRandomIntInclusive(sellIntervalMin.value,sellIntervalMax.value);
    logger.value.info('卖出',`等待 ${intervalTime}s`)
    await sleep(intervalTime*1000);
    if(lock){
      continue;
    }
    lock=true;
    try{
      if(status=='stop'){
        break;
      }
      const account=accounts.value.getSellAccount();
      await sell(account)
      if(status=='stop'){
        break;
      }
    } catch (error) {
      logger.value.error("卖出",error.message);
    }finally{
      lock=false;
    }
  }
}

function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

function getRandomFloatInclusive(min, max,decimal=5) {
    if (min > max) [min, max] = [max, min];
    // 通过极小值 Number.EPSILON 扩展范围
    const ret=Math.random() * (max - min + Number.EPSILON) + min;
    return parseFloat(ret.toFixed(decimal));
}

function getRandomIntInclusive(min, max,decimal=0) {
  if (min > max) [min, max] = [max, min];
  // 通过极小值 Number.EPSILON 扩展范围
  const ret=Math.random() * (max - min + Number.EPSILON) + min;
  return parseInt(ret);
}

const buy = async () => {
  try{
      logger.value.info("购买","购买开始...")
      // 1.创建市场账号
       const marketAccount=await client.createAccount()
       const new_account=accounts.value.saveAccount(marketAccount);
       const amount=buyMin.value==buyMax.value?buyMin.value:getRandomFloatInclusive(buyMin.value,buyMax.value,5);

       // 2.主号给市场账号转账 amount + gas 0.00025
       const mainAccount=client.getAccount(privateKey.value)
       await client.transfer(mainAccount,marketAccount.address,BigNumber(amount).plus(gas.value));

       // 3.市场账号购买
       await client.buy(
        marketAccount.privateKey,
         contractAddress.value,
         poolType.value,
        amount.toString(),
         slippage.value
        );
       await accounts.value.refreshAccount(new_account);
    logger.value.success("购买","购买完成")
  }catch(err){
    logger.value.error("购买","购买失败\n"+err.message)
    throw err;
  }

}

const createAccount=async()=>{
  return await client.createAccount()
}

const stopTrading = () => {
  status='stop';
  logger.value.error("交易","停止交易");
  indexRunning.value=false;
  logger.value.stop();
}



const parseWallet = async () => {
  if(!privateKey.value){
    ElMessage.error('请填写主钱包私钥地址');
    return null;
  }
  try{
    const account=await client.getAccount(privateKey.value);
    address.value = account.address;
    return account;
  }catch(e){
    ElMessage.error(e.message);
    return null;
  }
}

const isConnecting = ref(false);
const connectWallet = async () => {
  try {
    isConnecting.value = true;
    
    // 检查是否安装钱包
    if (!window.ethereum) {
      throw new Error('未检测到钱包扩展，请安装MetaMask');
    }

    // 请求账户连接
    const _accounts = await window.ethereum.request({
      method: 'eth_requestAccounts'
    });

    if (!_accounts || _accounts.length === 0) {
      throw new Error('未获取到钱包账户');
    }

    // 获取当前网络配置
    const chainId = await window.ethereum.request({ method: 'eth_chainId' });
    if (parseInt(chainId) !== getChainId(selectedChain.value)) {
      throw new Error(`请切换到${selectedChain.value.toUpperCase()}网络`);
    }

    address.value = _accounts[0];
    ElMessage.success(`钱包连接成功: ${formatAddress(address.value)}`);
    logger.value.success('钱包连接',`钱包：${address.value}`);
    
    
  } catch (error) {
    ElMessage.error(error.message || '钱包连接失败');
  } finally {
    isConnecting.value = false;
  }
};

const disconnectWallet = () => {
  logger.value.warning('钱包断开',`钱包：${address.value}`);
  address.value = '';
  ElMessage.success('钱包已断开连接');
};

const getChainId = (chain) => {
  const chainIds = {
    bsc: 56,
    base: 8453
  };
  return chainIds[chain];
};


const sell = async(storedAccount) => {
  try{
    if(storedAccount.token==0){
      await logger.value.error("卖出失败",`钱包: ${storedAccount.addr} \n代币数量为0`);
      return;
    }
    logger.value.warn('卖出',`卖出开始\n钱包: ${storedAccount.addr} \n代币: ${storedAccount.token}`);
    const marketAccount=await client.getAccount(storedAccount.privateKey);
     if(storedAccount.bnb<BigNumber(gas.value)){
       //主号给子号账号转Gas
       const mainAccount=client.getAccount(privateKey.value)
       await client.transfer(mainAccount,marketAccount.address,BigNumber(gas.value));
     }
     await client.sell(marketAccount.privateKey,contractAddress.value,poolType.value,storedAccount.token,slippage.value)
    //  await accounts.value.collectOne(storedAccount);
     await accounts.value.refreshAccount(storedAccount);
    await logger.value.success('卖出',`卖出完成\n钱包: ${storedAccount.addr} \n代币: ${storedAccount.token}`);
  }catch(error){
    await logger.value.error("卖出异常",error.message);
    console.log(error);
    throw error;
  }

}

const getAccountTokens = async(privateKey) => {
  const tokens=await client.getAccountTokens(privateKey,contractAddress.value);
  return tokens
}

const getAccountsBalances = async(walletAddresses) => {
  const tokens = [
  { symbol: 'BNB', address: '0x0000000000000000000000000000000000000101', decimals: 18 },
  { symbol: 'TOKEN', address: contractAddress.value, decimals: 18 },
  { symbol: 'USDT', address: '0x55d398326f99059fF775485246999027B3197955', decimals: 18 }
];
  const result=await client.getAccountsBalances(walletAddresses,tokens);
  console.log("getAccountsBalances:",result);
  return result
}


const getAccountBNB = async(privateKey) => {
  const tokens=await client.getAccountTokens(privateKey);
  return tokens[0];
}
const batchCollectBNB = async(privateKeys) => {
  // if(!privateKey.value){
  //   ElMessage.error('请填写主钱包私钥地址');
  //   return;
  // }
  // const mainAccount=client.getAccount(privateKey.value)
  if(!collectAddress.value){
    ElMessage.error('请填归集钱包地址');
    return;
  }
  // console.log('batchCollectBNB',privateKeys,collectAddress.value);
  await client.batchCollectBNB(privateKeys, collectAddress.value);
}

const batchCollectToken = async(privateKeys) => {
  const mainAccount=client.getAccount(privateKey.value)
  await client.batchCollectToken(privateKeys,contractAddress, mainAccount.address);
}


const getToken = async()=>{
  if(!contractAddress.value){
    return 0;
  }
  const tk= await client.getToken(contractAddress.value,poolType.value)
  token.value=tk;
  return tk;
}



const addToMetamask = (primaryKey) => {
  client.addToMetamask(primaryKey);
}

const handleMode = (value) => {
  let modeName= '';
  if(value=='pump') modeName='买入';
  if(value=='dump') modeName='卖出';
  if(value=='mixed') modeName='混合';
  logger.value.warn("配置",`切换 [${modeName}] 模式`)
  logger.value.updateMode(value);
}


BigInt.prototype.toJSON = function() {
  return this.toString();
};

const getConfig=()=>{
  return {
    chain:chain.value,
    rpc:rpc.value,
    mode:mode.value,
    contractAddress:contractAddress.value,
    poolType:poolType.value,
    targetPrice:targetPrice.value,
    buyMethod:buyMethod.value,
    buyMin:buyMin.value,
    buyMax:buyMax.value,
    buyIntervalMin:buyIntervalMin.value,
    buyIntervalMax:buyIntervalMax.value,
    slippage:slippage.value,
    enableConcurrent:enableConcurrent.value,
  }
}

defineExpose({
  startTrading,
  stopTrading,
  getConfig,
  parseWallet,
  connectWallet,
  disconnectWallet,
  createAccount,
  sell,
  getAccountTokens,
  getAccountBNB,
  batchCollectBNB,
  batchCollectToken,
  addToMetamask,
  getAccountsBalances,
  getToken
  })
</script>

<style scoped>
.trade{
  margin-top: 10px;
}
.unit {
  margin-left: 10px;
  color: #666;
}
.execution-control {
  margin-top: 20px;
  text-align: right;
}
</style>